package com.lambkit.db;

import com.lambkit.db.dialect.IDialect;
import com.lambkit.db.sql.Example;

import java.util.List;
import java.util.concurrent.Future;

/**
 * @author yangyong(孤竹行)
 */
public interface IDbOpt<T extends IRowData, P extends IPageData<T>>  {
    IDialect getDialect();
    void setDialect(IDialect dialect);
    //////////////////////////////////////////////////////////////////////

    T findFirst(Sql sqlPara);
    T findFirst(Example example);

    List<T> find(Sql sqlPara);
    List<T> find(Example example);
    List<T> find(Example example, Integer count);

    List<T> findAll(String tableName);
    /**
     * 分页查询数据
     * @return
     */
    P paginate(Integer pageNumber, Integer pageSize, Sql sqlPara);
    P paginate(Integer pageNumber, Integer pageSize, Example example);

    P paginate(Sql sqlPara, Integer offset, Integer limit);
    P paginate(Example example, Integer offset, Integer limit);

    Long count(Sql sqlPara);
    Long count(Example example);
    boolean save(T rowData);
    boolean delete(T rowData);
    //boolean deleteById(Object id);
    //boolean deleteByIds(Object ...ids);
    int delete(Sql sqlPara);
    int delete(Example example);
    boolean update(T rowData);
    int update(Sql sqlPara);

    boolean saveOrUpdate(T rowData);
    /**
     * Execute update, insert or delete sql statement.
     * @param sql an SQL statement that may contain one or more '?' IN parameter placeholders
     * @param paras the parameters of sql
     * @return either the row count for <code>INSERT</code>, <code>UPDATE</code>,
     *         or <code>DELETE</code> statements, or 0 for SQL statements
     *         that return nothing
     */
    int update(String sql, Object... paras);

    /**
     * @see #update(String, Object...)
     * @param sql an SQL statement
     */
    int update(String sql);

    /**
     * @see #find(String, Object...)
     */
    List<T> find(String sql, Object... paras);

    /**
     * @see #find(String, Object...)
     * @param sql the sql statement
     */
    List<T> find(String sql);

    List<T> find(T rowData);
    /**
     * Find first rowData. I recommend add "limit 1" in your sql.
     * @param sql an SQL statement that may contain one or more '?' IN parameter placeholders
     * @param paras the parameters of sql
     * @return the IRowData object
     */
    T findFirst(String sql, Object... paras);

    /**
     * @see #findFirst(String, Object...)
     * @param sql an SQL statement
     */
    T findFirst(String sql);

    T findFirst(T rowData);

    /**
     * Find rowData by ids.
     * <pre>
     * Example:
     * IRowData user = findById("user", "user_id", 123);
     * IRowData userRole = findById("user_role", "user_id, role_id", 123, 456);
     * </pre>
     * @param idValues the id value of the rowData, it can be composite id values
     */
    T findById(String table, String primaryKeys, Object... idValues);

    /**
     * Execute delete sql statement.
     * @param sql an SQL statement that may contain one or more '?' IN parameter placeholders
     * @param paras the parameters of sql
     * @return the row count for <code>DELETE</code> statements, or 0 for SQL statements
     *         that return nothing
     */
    int delete(String sql, Object... paras);

    /**
     * @see #delete(String, Object...)
     * @param sql an SQL statement
     */
    int delete(String sql);

    int deleteById(String table, String primaryKeys, Object... idValues);

    /**
     * Execute transaction with default transaction level.
     * @see #tx(Integer, ITxAction< V >)
     */
    <V extends IDbOpt> boolean tx(ITxAction<V> atom);

    <V extends IDbOpt> boolean tx(Integer transactionLevel, ITxAction<V> atom);

    /**
     * 主要用于嵌套事务场景
     */
    <V extends IDbOpt> Future<Boolean> txInNewThread(ITxAction<V> atom);

    <V extends IDbOpt> Future<Boolean> txInNewThread(Integer transactionLevel, ITxAction<V> atom);

    /**
     * Find IRowData by cache.
     * @see #find(String, Object...)
     * @param cacheName the cache name
     * @param key the key used to get date from cache
     * @return the list of IRowData
     */
    List<T> findByCache(String cacheName, Object key, Sql sqlPara);

    List<T> findByCache(String cacheName, Object key, Example example);

    /**
     * Find first rowData by cache. I recommend add "limit 1" in your sql.
     * @see #findFirst(String, Object...)
     * @param cacheName the cache name
     * @param key the key used to get date from cache
     * @return the T object
     */
    T findFirstByCache(String cacheName, Object key, Sql sqlPara);

    T findFirstByCache(String cacheName, Object key, Example example);

    /**
     * Paginate by cache.
     * @return IPageData
     */
    P paginateByCache(String cacheName, Object key, int pageNumber, int pageSize, Sql sqlPara);

    P paginateByCache(String cacheName, Object key, int pageNumber, int pageSize, Example example);

    /**
     * Execute a batch of SQL INSERT, UPDATE, or DELETE queries.
     * <pre>
     * Example:
     * String sql = "insert into user(name, cash) values(?, ?)";
     * int[] result = batch(sql, new Object[][]{{"aaa", 666}, {"aaa", 666}});
     * </pre>
     * @param sql The SQL to execute.
     * @param paras An array of query replacement parameters.  Each row in this array is one set of batch replacement values.
     * @return The number of rows updated per statement
     */
    int[] batch(String sql, Object[][] paras, int batchSize);

    /**
     * Execute a batch of SQL INSERT, UPDATE, or DELETE queries.
     * <pre>
     * Example:
     * int[] result = batch(sqlList, 500);
     * </pre>
     * @param sqlList The SQL list to execute.
     * @param batchSize batch size.
     * @return The number of rows updated per statement
     */
    int[] batch(List<String> sqlList, int batchSize);

    /**
     * Batch save records using the "insert into ..." sql generated by the first rowData in recordList.
     * Ensure all the rowData can use the same sql as the first rowData.
     */
    int[] batchSave(List<T> recordList, int batchSize);

    /**
     * Batch update models using the attrs names of the first model in modelList.
     * Ensure all the models can use the same sql as the first model.
     */
    int[] batchUpdate(List<T> recordList, int batchSize);

    /*
    基于模板语言实现

    Sql getSql(String key, M rowData);

    Sql getSql(String key, Map data);

    Sql getSql(String key, Object... paras);

    Sql getSqlByString(String content, Map data);

    Sql getSqlByString(String content, Object... paras);
    */

    /**
     * 迭代处理每一个查询出来的 IRowData 对象
     * <pre>
     * 例子：
     * each(rowData -> {
     *    // 处理 rowData 的代码在此
     *
     *    // 返回 true 继续循环处理下一条数据，返回 false 立即终止循环
     *    return true;
     * }, sql, paras);
     * </pre>
     */
    //void each(Function<T, Boolean> func, String sql, Object... paras);
}
